﻿using System.Collections.Generic;
using System.ComponentModel;
using System;
using System.Collections;
using System.Linq;

namespace YYS
{
    /// <summary>
    /// Model : INotifyPropertyChanged, IEditableObject, IDataErrorInfo
    /// </summary>
    public abstract class Model : INotifyPropertyChanged, IEditableObject, IDataErrorInfo, ICloneable
    {
        bool _IsChecked = false;
        bool _IsEnabled = false;
        string _BaseRemark = "";

        /// <summary>
        /// 可选
        /// </summary>
        public bool IsChecked
        {
            get { return _IsChecked; }
            set
            {
                _IsChecked = value;
                this.RaisePropertyChanged(x => x.IsChecked);
            }
        }

        /// <summary>
        /// 可用
        /// </summary>
        public bool IsEnabled
        {
            get { return _IsEnabled; }
            set
            {
                _IsEnabled = value;
                this.RaisePropertyChanged(x => x.IsEnabled);
            }
        }

        /// <summary>
        /// 基础备注
        /// </summary>
        public string BaseRemark
        {
            get { return _BaseRemark; }
            set
            {
                _BaseRemark = value;
                this.RaisePropertyChanged(x => x.BaseRemark);
            }
        }


        #region IDataErrorInfo

        /// <summary>
        /// 清除错误信息
        /// </summary>
        public void ClearError()
        {
            Errors.Clear();
        }
        /// <summary>
        /// 验证实体
        /// </summary>
        public string Valid()
        {
            ClearError();
            return this.Valid(x => new { }, false);
        }

        /// <summary>
        /// 验证字段是否有效
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        protected virtual string ValidField(string columnName)
        {
            return Attr.validField(this, columnName);
        }

        /// <summary>
        /// 验证错误
        /// </summary>
        public string Error
        {
            get
            {
                string _Error = null;
                if (Errors.Count > 0)
                {
                    System.Text.StringBuilder sb = new System.Text.StringBuilder();
                    Errors.jEach((x) =>
                    {
                        if (sb.Length > 0)
                        {
                            sb.Append(" | ");
                        }
                        sb.Append(x.Value);
                    });
                    _Error = sb.ToString();
                }
                return _Error;
            }
        }
        /// <summary>
        /// IDataErrorInfo
        /// </summary>
        /// <param name="columnName">属性名</param>
        /// <returns></returns>
        public virtual string this[string columnName]
        {
            get
            {
                return ValidProperty(columnName);
            }
        }

        protected System.Collections.Generic.Dictionary<string, string> Errors = new Dictionary<string, string>();

        /// <summary>
        /// 添加一条错误信息 
        /// 当ErrMsg为null时从错误信息中移除，否则新增
        /// </summary>
        /// <param name="columnName">错误信息标识</param>
        /// <param name="ErrMsg">错误信息</param>
        protected void AddError(string columnName, string ErrMsg)
        {
            if (ErrMsg.jIsNullOrWhiteSpace())
            {
                if (Errors.ContainsKey(columnName))
                {
                    Errors.Remove(columnName);
                }
            }
            else
            {
                if (Errors.ContainsKey(columnName))
                {
                    Errors[columnName] = ErrMsg;
                }
                else
                {
                    Errors.Add(columnName, ErrMsg);
                }
            }
        }
        /// <summary>
        /// 验证单个属性
        /// </summary>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public string ValidProperty(string columnName)
        {
            string ErrMsg = ValidField(columnName);
            AddError(columnName, ErrMsg);
            RaisePropertyChanged("Error");
            RaisePropertyChanged("HasError");
            return ErrMsg;
        }
        /// <summary>
        /// 是否包含验证错误
        /// </summary>
        public bool HasError
        {
            get
            {
                return Errors.Count > 0 ? true : false;
            }
        }
        #endregion

        #region IEditableObject
        /// <summary>
        /// 开始编辑
        /// </summary>
        public void BeginEdit()
        {
            if (!inTX)
            {
                Backup();
                inTX = true;
            }
        }
        /// <summary>
        /// 取消编辑
        /// </summary>
        public void CancelEdit()
        {
            if (inTX)
            {
                Restore();
                inTX = false;
            }
        }
        /// <summary>
        /// 结束编辑
        /// </summary>
        public void EndEdit()
        {
            if (inTX)
            {
                if (Map != null)
                {
                    Map.Clear();
                }
                inTX = false;
            }
        }
        bool inTX = false;
        System.Collections.Generic.Dictionary<string, object> Map;
        void Backup()
        {
            if (Map == null) Map = new Dictionary<string, object>();
            System.Type type = this.GetType();
            System.Reflection.PropertyInfo[] properties = type.GetProperties();
            foreach (System.Reflection.PropertyInfo property in properties)
            {
                if (property.CanRead && property.CanWrite)
                {
                    if (Map.ContainsKey(property.Name)) Map[property.Name] = property.GetValue(this, null);
                    else Map.Add(property.Name, property.GetValue(this, null));
                }
            }
        }
        void Restore()
        {
            System.Type type = this.GetType();
            System.Reflection.PropertyInfo[] properties = type.GetProperties();
            foreach (System.Reflection.PropertyInfo property in properties)
            {
                if (property.CanWrite)
                {
                    if (Map.ContainsKey(property.Name))
                    {
                        property.SetValue(this, Map[property.Name], null);
                    }
                }
                RaisePropertyChanged(property.Name);
            }
        }
        #endregion

        #region INotifyPropertyChanged
        /// <summary>
        /// PropertyChanged
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// INotifyPropertyChanged 通知属性改变
        /// </summary>
        /// <param name="propertyName"></param>
        public void RaisePropertyChanged(string propertyName)
        {
            if (propertyName != "Error" && propertyName != "HasError")
            {
                ValidProperty(propertyName);
            }
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion


        /// <summary>
        /// 使用NewModel 赋值本实体属性
        /// </summary>
        /// <param name="NewModel"></param>
        public void Update(Model NewModel = null)
        {
            this.Update(NewModel, x => new { });
        }

        /// <summary>
        /// 创建当前对象的浅表副本
        /// </summary>
        /// <returns></returns>
        public virtual object Clone()
        {
            return this.MemberwiseClone();
        }
    }
    /// <summary>
    /// Model扩展方法
    /// </summary>
    public static class PropertyChangedBaseEx
    {
        /// <summary>
        /// RaisePropertyChanged(string propertyName) 扩展方法
        /// this.RaisePropertyChanged(x => x.属性); 关键字 this 不能省略
        /// </summary>
        public static void RaisePropertyChanged<T, TProperty>(this T propertyChangedBase, System.Linq.Expressions.Expression<Func<T, TProperty>> expression) where T : Model
        {
            if (expression == null) return;

            if (expression.Body is System.Linq.Expressions.NewExpression)
            {
                var newExpression = expression.Body as System.Linq.Expressions.NewExpression;
                if (newExpression.Members != null && newExpression.Members.Count > 0)
                {
                    foreach (var x in newExpression.Members)
                    {
                        propertyChangedBase.RaisePropertyChanged(x.Name);
                    }
                }
            }
            else if (expression.Body is System.Linq.Expressions.MemberExpression)
            {
                var memberExpression = expression.Body as System.Linq.Expressions.MemberExpression;
                propertyChangedBase.RaisePropertyChanged(memberExpression.Member.Name);
            }
            /*
            var memberExpression = expression.Body as System.Linq.Expressions.MemberExpression;
            if (memberExpression != null)
            {
                string propertyName = memberExpression.Member.Name;
                propertyChangedBase.RaisePropertyChanged(propertyName);
            }
            else
                throw new NotImplementedException();
            */
        }

        /// <summary>
        /// 更新实体
        /// </summary>
        /// <typeparam name="T">Model</typeparam>
        /// <typeparam name="TProperty">Model属性</typeparam>
        /// <param name="Entity">Model</param>
        /// <param name="newModel">新Model</param>
        /// <param name="linqExpression">不包含的属性 x => x.PropertyA 或 new { x.PropertyA, x.PropertyB }</param>
        public static void Update<T, TProperty>(this T Entity, T newModel = null, System.Linq.Expressions.Expression<Func<T, TProperty>> linqExpression = null) where T : Model
        {
            if (newModel == null) return;

            List<string> NotContainProperties = new List<string>();
            if (linqExpression != null)
            {
                if (linqExpression.Body is System.Linq.Expressions.NewExpression)
                {
                    var x1 = linqExpression.Body as System.Linq.Expressions.NewExpression;
                    if (x1.Members != null && x1.Members.Count > 0)
                    {
                        foreach (var x in x1.Members)
                        {
                            NotContainProperties.Add(x.Name);
                        }
                    }
                }
                else if (linqExpression.Body is System.Linq.Expressions.MemberExpression)
                {
                    var x2 = linqExpression.Body as System.Linq.Expressions.MemberExpression;
                    NotContainProperties.Add(x2.Member.Name);
                }
            }
            System.Type type = Entity.GetType();
            System.Reflection.PropertyInfo[] properties = type.GetProperties();

            var newType = newModel.GetType();

            foreach (System.Reflection.PropertyInfo property in properties)
            {
                if (NotContainProperties.Contains(property.Name)) continue;

                if (property.CanWrite)
                {
                    var newProp = newType.GetProperty(property.Name);
                    if (newProp != null && newProp.CanRead)
                    {
                        property.SetValue(Entity, newProp.GetValue(newModel, null), null);
                    }
                }
                Entity.RaisePropertyChanged(property.Name);
            }
        }

        /// <summary>
        /// 验证Model
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="Entity"></param>
        /// <param name="linqExpression"></param>
        /// <param name="IsContainOrExcept">当为True时，只验证选中的属性；为False时，不验证选中的属性</param>
        /// <returns></returns>
        public static string Valid<T, TProperty>(this T Entity, System.Linq.Expressions.Expression<Func<T, TProperty>> linqExpression = null, bool IsContainOrExcept = false) where T : Model
        {
            Entity.ClearError();
            List<string> NotContainProperties = new List<string>();
            if (linqExpression != null)
            {
                if (linqExpression.Body is System.Linq.Expressions.NewExpression)
                {
                    var x1 = linqExpression.Body as System.Linq.Expressions.NewExpression;
                    if (x1.Members != null && x1.Members.Count > 0)
                    {
                        foreach (var x in x1.Members)
                        {
                            NotContainProperties.Add(x.Name);
                        }
                    }
                }
                else if (linqExpression.Body is System.Linq.Expressions.MemberExpression)
                {
                    var x2 = linqExpression.Body as System.Linq.Expressions.MemberExpression;
                    NotContainProperties.Add(x2.Member.Name);
                }
            }
            System.Type type = Entity.GetType();
            System.Reflection.PropertyInfo[] properties = type.GetProperties();
            foreach (System.Reflection.PropertyInfo property in properties)
            {
                if (IsContainOrExcept)
                {
                    if (NotContainProperties.Contains(property.Name))
                    {
                        Entity.ValidProperty(property.Name);
                    }
                }
                else
                {
                    if (!NotContainProperties.Contains(property.Name))
                    {
                        Entity.ValidProperty(property.Name);
                    }
                }
            }
            return Entity.Error;
        }

        /// <summary>
        /// 获取属性名称
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="obj"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string GetPropertyName<T, TProperty>(this T obj, System.Linq.Expressions.Expression<Func<T, TProperty>> expression) where T : new()
        {
            var memberExpression = expression.Body as System.Linq.Expressions.MemberExpression;
            if (memberExpression != null)
            {
                return memberExpression.Member.Name;
            }
            return null;
        }

        /// <summary>
        /// 判断是否某个属性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="propertyChangedBase"></param>
        /// <param name="expression"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static bool IsProperty<T, TProperty>(this T propertyChangedBase, System.Linq.Expressions.Expression<Func<T, TProperty>> expression, string propertyName) where T : Model
        {
            var memberExpression = expression.Body as System.Linq.Expressions.MemberExpression;
            return memberExpression == null ? false : (memberExpression.Member.Name == propertyName);
        }

        /// <summary>
        /// 调用string ValidField(string columnName)进行属性验证
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TProperty"></typeparam>
        /// <param name="propertyValidBase"></param>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string ValidProperty<T, TProperty>(this T propertyValidBase, System.Linq.Expressions.Expression<Func<T, TProperty>> expression) where T : Model
        {
            var memberExpression = expression.Body as System.Linq.Expressions.MemberExpression;
            if (memberExpression != null)
            {
                string propertyName = memberExpression.Member.Name;
                return propertyValidBase.ValidProperty(propertyName);
            }
            else
                return null;
        }

        /// <summary>
        /// 创建当前对象的浅表副本
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T Copy<T>(this T value) where T : Model
        {
            return value.Clone() as T;
        }
    }
}
